﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace JobShop
{
    public class TcGeneration
    {
        private readonly TcProperties Properties;
        public Int32 Index { get; private set; }
        public TcSolution Best { get; private set; }
        public TcSolution Worst { get; private set; }
        public Double Average { get; private set; }
        public Double StDev { get; private set; }
        public Int32 Median { get; private set; }
        
        private TcIndividual[] Individuals { get; set; }
        //------------------------------------------------------------------------------

        public TcGeneration(Int32 prmIndex, TcProperties prmProperties)
        {
            Index = prmIndex;
            Properties = prmProperties;
            Best = null;
            Worst = null;
            Average = default(Double);
            StDev = default(Double);
            Median = default(Int32);
            Individuals = new TcIndividual[Properties.PopSize];
        }
        //------------------------------------------------------------------------------

        public void CreateInitialPopulation(TcProblem prmProblem)
        {
            // Create the initial population.
            for (int i = 0; i < Properties.PopSize; i++)
                Individuals[i] = new TcIndividual(prmProblem);
        }
        //------------------------------------------------------------------------------

        public void Evaluate()
        {
            Int32[] fitnessValues = new Int32[Properties.PopSize];
            for (int i = 0; i < Properties.PopSize; i++)
                fitnessValues[i] = Individuals[i].Evaluate();

            Array.Sort(Individuals);
            Average = fitnessValues.Average();
            Median = Individuals[Properties.PopSize / 2].Fitness;
            StDev = fitnessValues.Aggregate(0.0, (result, iter) => { return Math.Abs(result + (iter - Average)); });
            Best = Individuals.First().Solution.Duplicate();
            Worst = Individuals.Last().Solution.Duplicate();
        }
        //------------------------------------------------------------------------------

        public TcGeneration Reproduce()
        {
            Random rand = new Random();
            TcGeneration newGen = new TcGeneration(Index + 1, Properties);
            int start = 0;

            if (Properties.PreserveBest)
            {
                newGen.Individuals[0] = Individuals[0].Duplicate();
                start = 1;
            }

            for (int i = start; i < Properties.PopSize; i++)
            {
                Double val = rand.NextDouble();
                if (val >= 0.0f && val < Properties.XoverRate)
                {
                    switch (Properties.XoverType)
                    {
                        case TeCrossoverType.OnePoint:
                            newGen.Individuals[i] = OnePointTournamentCrossover();
                            break;

                        case TeCrossoverType.TwoPoint:
                            newGen.Individuals[i] = TwoPointTournamentCrossover();
                            break;
                    }
                    
                }
                else if (val >= Properties.XoverRate && val < (Properties.XoverRate + Properties.MutationRate))
                {
                    Int32 bits = Properties.MutationBits;
                    if (Properties.UseProgressiveMutation)
                    {
                        bits = Convert.ToInt32(Math.Ceiling(((Individuals[i].Fitness - Best.Fitness) * 0.5 / Individuals[i].Fitness) * Individuals[i].Chromosome.Length));
                        bits = (bits > Individuals[i].Chromosome.Length / 10) ? Individuals[i].Chromosome.Length / 10 : bits;
                    }

                    switch (Properties.MutationType)
                    {
                        case TeMutationType.BitFlip:
                            newGen.Individuals[i] = BitFlipMutation(bits, Individuals[i]);
                            break;
                    }
                }
                else
                {
                    newGen.Individuals[i] = Individuals[i].Duplicate();
                }
            }
            return newGen;
        }
        //------------------------------------------------------------------------------

        private Tuple<TcIndividual, TcIndividual> ChosenIndividuals()
        {
            Random rand = new Random();
            Int32 elitPoint = Convert.ToInt32(Properties.ElitismRate * Properties.PopSize);
            TcIndividual elit = Individuals[rand.Next(0, elitPoint - 1)];
            TcIndividual item1 = Individuals[rand.Next(elitPoint, Properties.PopSize - 1)];
            TcIndividual item2 = Individuals[rand.Next(elitPoint, Properties.PopSize - 1)];
            TcIndividual winner = item1.Fitness < item2.Fitness ? item1 : item2;
            return new Tuple<TcIndividual, TcIndividual>(elit, winner);
        }
        //------------------------------------------------------------------------------

        private TcIndividual OnePointTournamentCrossover()
        {
            Tuple<TcIndividual, TcIndividual> pair = ChosenIndividuals();
            TcIndividual newIndiv = pair.Item1.Duplicate();
            newIndiv.OnePointBinaryCrossover(new Random().Next(1, Properties.PopSize - 2), pair.Item2);
            return newIndiv;
        }
        //------------------------------------------------------------------------------

        private TcIndividual TwoPointTournamentCrossover()
        {
            Tuple<TcIndividual, TcIndividual> pair = ChosenIndividuals();
            TcIndividual newIndiv = pair.Item1.Duplicate();
            Random rnd = new Random();
            Int32 p1 = rnd.Next(1, Properties.PopSize * 2 / 3);
            newIndiv.TwoPointBinaryCrossover(p1, rnd.Next(p1 + 1, Properties.PopSize - 2), pair.Item2);
            return newIndiv;
        }
        //------------------------------------------------------------------------------

        private TcIndividual BitFlipMutation(Int32 prmBits, TcIndividual prmIndiv)
        {
            TcIndividual newIndiv = prmIndiv.Duplicate();
            newIndiv.BitFlipMutation(prmBits);
            return newIndiv;
        }
        //------------------------------------------------------------------------------

        private IEnumerable<TcIndividual> OrderedEvaluatedIndividuals()
        {
            foreach (TcIndividual indiv in Individuals)
                indiv.Evaluate();
            return Individuals.OrderBy(iter => iter.Fitness);
        }
        //------------------------------------------------------------------------------

    }
    //------------------------------------------------------------------------------

}
//------------------------------------------------------------------------------